// post.cc 
// 	Routines to deliver incoming network messages to the correct
//	"address" -- a mailbox, or a holding area for incoming messages.
//	This module operates just like the US postal service (in other
//	words, it works, but it's slow, and you can't really be sure if
//	your mail really got through!).
//
//	Note that once we prepend the MailHdr to the outgoing message data,
//	the combination (MailHdr plus data) looks like "data" to the Network 
//	device.
//
// 	The implementation synchronizes incoming messages with threads
//	waiting for those messages.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "post.h"
#include "system.h"
#include <sstream>

extern "C" {
	int bcopy(char *, char *, int);
};

string itoa(int input) {
	std::stringstream ss;
	ss<<input;
	return ss.str();
}


void forward(int toServer, int fromClientPacket, int fromClientMail, string toSend){

		outPacketHeader.to = toServer;//server address hardcoded to zero
        outMailHeader.to = 0;
        outMailHeader.from = fromClientMail;//given at startup
        outPacketHeader.from = fromClientPacket;
        char *data = new char[40];

       
        strcpy(data,toSend.c_str());
        outMailHeader.length = strlen(data)+1;
        
        if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
                printf("Can't contact server\n");
                interrupt->Halt();
        }
        

}




void StartServer(int addr){

	
while(true){

		outPacketHeader.to = 0;//the server address
		outMailHeader.to = 0;
		outMailHeader.from = 1;
		        char *data = "default";

		outMailHeader.length = strlen(data)+1;
		
		printf("Waiting for message to address: %d\n",netname);
		
		postOffice->Receive(0,&inPacketHeader,&inMailHeader,buffer_net);
		
		printf("Got \"%s\" from %d, box %d\n",buffer_net,inPacketHeader.from,inMailHeader.from);
		
		fflush(stdout);
		net_buffer = buffer_net;
		
//****Client Server Messages****
		if((net_buffer.find("createLock"))!=string::npos){//none
		//put on pending queue
		//when adding to pending queue store client info
		//
		int found = net_buffer.find(".");
		string name = net_buffer.substr(found+1);
		serverCreateLock(name,false);
		}
		
		//searchLock
		//recieves  -1 or index value
		//if -1 then increment value in pending shit and send back if need be
		
		else if((net_buffer.find("searchLock"))!=string::npos){//one
		int found = net_buffer.find(".");
		string name = net_buffer.substr(found+1);
		searchLock(name);}
		
		else if((net_buffer.find("returnSearch"))!=string::npos){//two
			int found = net_buffer.find(".");
			int found2 = net_buffer.find(",");
			int number1 = atoi(net_buffer.substr(found+1,found2-found).c_str());
			string name = net_buffer.substr(found2+1);
			updateCreate(number1,name);
		}
		
		else if((net_buffer.find("destroyLock"))!=string::npos){//one
		int found = net_buffer.find(".");
		int number1 = atoi(net_buffer.substr(found+1).c_str());
		serverDestroyLock(number1);}
		
		else if((net_buffer.find("acquireLock"))!=string::npos){//one
		int found = net_buffer.find(".");
		int number1 = atoi(net_buffer.substr(found+1).c_str());
		serverAcquireLock(number1);}
		
		else if((net_buffer.find("releaseLock"))!=string::npos){//one
		int found = net_buffer.find(".");
		int number1 = atoi(net_buffer.substr(found+1).c_str());
		serverReleaseLock(number1);}
		
		else if((net_buffer.find("createCondition"))!=string::npos)//none
		serverCreateCondition();
		
		else if((net_buffer.find("destroyCondition"))!=string::npos){//one
		int found = net_buffer.find(".");
		int number1 = atoi(net_buffer.substr(found+1).c_str());
		serverDestroyCondition(number1);}
		
		else if((net_buffer.find("signalCondition"))!=string::npos){//two
		int found = net_buffer.find(".");
			int found2 = net_buffer.find(",");
			int number1 = atoi(net_buffer.substr(found+1,found2-found).c_str());
			int number2 = atoi(net_buffer.substr(found2+1).c_str());
			serverSignalCondition(number1,number2);
		}
		
		else if((net_buffer.find("waitCondition"))!=string::npos){//two
		int found = net_buffer.find(".");
			int found2 = net_buffer.find(",");
			int number1 = atoi(net_buffer.substr(found+1,found2-found).c_str());
			int number2 = atoi(net_buffer.substr(found2+1).c_str());
			serverWaitCondition(number1,number2);
		}
		
		else if((net_buffer.find("broadcastCondition"))!=string::npos){//two
		int found = net_buffer.find(".");
			int found2 = net_buffer.find(",");
			int number1 = atoi(net_buffer.substr(found+1,found2-found).c_str());
			int number2 = atoi(net_buffer.substr(found2+1).c_str());
			serverBroadcastCondition(number1,number2);
		}
		
		else if((net_buffer.find("createMonitor"))!=string::npos)//none
		serverCreateMonitor();
		
		else if((net_buffer.find("retrieveMonitor"))!=string::npos){//one
		int found = net_buffer.find(".");
		int number1 = atoi(net_buffer.substr(found+1).c_str());
		serverRetrieveMonitor(number1);}
		
		else if((net_buffer.find("setMonitor"))!=string::npos){//two
		int found = net_buffer.find(".");
			int found2 = net_buffer.find(",");
			int number1 = atoi(net_buffer.substr(found+1,found2-found).c_str());
			int number2 = atoi(net_buffer.substr(found2+1).c_str());
			serverSetMonitor(number1,number2);
		}




 
}

}


void updateCreate(int index, string name){
int i;
//find the name of the thing in create list
for(i = 0; i < MAXLOCKS; i++){
if(creates[i].open == false && creates[i].name == name){
//when found if index = -1 then increment 
break;
}

}

if(index == -1)
creates[i].rejects++;

//respond if rejects is full create it
//found the lock

if(index != -1){
//respond to client
		net_buffer = itoa(index);
		strcpy(data,net_buffer.c_str());

	outPacketHeader.to = creates[i].clientPacket;//fix this shat
	outMailHeader.to = creates[i].clientMail;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
	
	
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);

}

if((creates[i].rejects - 1) > numberOfServers){
//create it
serverCreateLock(name,true);
}



}

void searchLock(string name){

bool found = false;
int i;

for(i = 0; i < MAXLOCKS; i++){
if(KernelLocks[i].open == false && KernelLocks[i].name == name){
//we found the lock and it has been created
found = true;
break;

}
}

if(!found)
i = -1;

        char *data = new char[40];

		net_buffer = "returnSearch";
        net_buffer.push_back('.');
        net_buffer.append(itoa(i));
        net_buffer.push_back(',');
        net_buffer.append(name);
        //return index and name of lock
        strcpy(data,net_buffer.c_str());

//send back reply to server
	outPacketHeader.to = inPacketHeader.from;
	outMailHeader.to = inMailHeader.from;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
		
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);

	


}



void serverCreateLock(string name, bool create){
//NEED TO FIO WHAT HAPPENS IF ONLY 1 SERVER IS ALIVE

if(numberOfServers == 1)
create = true;

if(create == false){

//find a free create slot
for(int i = 0; i < MAXLOCKS; i++){
if(creates[i].open == true){
creates[i].name = name;
creates[i].rejects = 0;
creates[i].clientPacket = inPacketHeader.from;
creates[i].clientMail = inMailHeader.from;
creates[i].open = false;

}
}
char *data = new char[40];

//search all the other servers to see if its already been created
string search = "searchLock"
search.push_back('.');
search.append(name);
strcpy(data,search.c_str());

//search all the other servers before getting a location

//send requests to all the other servers
for(int i = 0; i < numberOfServers; i++){
//need a way to get addresses of all other servers
if(i != serverNumber){
outPacketHeader.to = i;//send to other server
outMailHeader.to = 0;
outMailHeader.from = serverNumber;
outPacketHeader.from = 0;//given at startup

if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
                printf("Can't contact server\n");
                interrupt->Halt();}
                
                }}



}
else{
//find it in create list and create it
int i;
for(i = 0; i < MAXLOCKS; i++){
if(creates[i].open == false && creates[i].name == name)

}



KLockArrayLock->Acquire();
	int freeLock = -1;//find a free lock
	for(int i = serverNumber *1000; i < (serverNumber*1000 + 999);i++){
		if(KernelLocks[i].open == true ){
			freeLock = i;
			break;}
	}

	if(freeLock == -1){
		KLockArrayLock->Release();
		//return freeLock;
		}

	else{
		//set it to not free
		KernelLocks[freeLock].addrSpace = currentThread->space;
		KernelLocks[freeLock].open = false;
		//create lock
		KernelLocks[freeLock].lock = new Lock(name);
		//return index
		KernelLocks[freeLock].name = name;
		KLockArrayLock->Release();
		printf("CREATING LOCK %d\n",freeLock);
		//return freeLock;
		}
		
		char *data = "default";

		net_buffer = itoa(freeLock);
		strcpy(data,net_buffer.c_str());

	
	
	
	outPacketHeader.to = inPacketHeader.from;//fix this shat
	outMailHeader.to = inMailHeader.from;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
	
	printf("Sending '%s\' to %d\n",data,outMailHeader.from);//take out after testing
	
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);
}}

void serverDestroyLock(int number){






KLockArrayLock->Acquire();


	//make sure number is valid
	if(KernelLocks[number].open == false){
		//make sure lock doesnt need to be used
		if(KernelLocks[number].usage > 0){//nti usage...cannot destroy lock because it is being used
			KernelLocks[number].destroyable = true;
			KLockArrayLock->Release();
				printf("INVALID LOCK NUMBER!\n");
}
		else{
			KernelLocks[number].lock = NULL;
			KernelLocks[number].open = true;//set to open
			KLockArrayLock->Release();

		}

	}
	else 
	printf("INVALID LOCK NUMBER!\n");
	
	KLockArrayLock->Release();
	
	
				char *data = "success";

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);


}


void serverAcquireLock(int number){
	KLockArrayLock->Acquire();
if(KernelLocks[number].open == false){

int toServer = number / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend = "acquireLock";
toSend.push_back('.');
toSend.append(itoa(number));

forward(toServer, fromClientPacket, fromClientMail, toSend);

		KLockArrayLock->Release();
	}
else{
bool wait = true;

//perform checks to make sure lock exists
	//lock exists
	//in current space
	//increment usage
	//call lock.acquire
	if(number >= MAXLOCKS){
		printf("Lock is out or range!\n");
		KLockArrayLock->Release();
	}

	
	
	else{
		KLockArrayLock->Release();
		if(KernelLocks[number].lock->lock_queue->Size() == 0){
		wait = false;}
		KernelLocks[number].lock->lock_queue->Append(&inMailHeader.from);

	}

//if no one on the queue then put myself on and send success
//if someone is on the queue the put myself on and dont send



printf("Size of list is %d so packet from = %d mail from = %d\n",KernelLocks[number].lock->lock_queue->Size(),inPacketHeader.from,inMailHeader.from);

	if(!wait){
				char *data = "success";
	printf("Waking up %d %d",inPacketHeader.from,inMailHeader.from);
		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);

}
}
}


void serverReleaseLock(int number){

if(KernelLocks[number].open == true){
	

int toServer = number / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend = "releaseLock";
toSend.push_back('.');
toSend.append(itoa(number));

forward(toServer, fromClientPacket, fromClientMail, toSend);

}

else{

int *toSend = &inMailHeader.from;
//perform checks to make sure lock exists
	//lock exists
	//in current space
	//increment usage
	//call lock.release
	KLockArrayLock->Acquire();
	if(number >= MAXLOCKS){
		printf("Lock is out or range!\n");
		KLockArrayLock->Release();
	}

	
	
	else{
		KLockArrayLock->Release();
		//KernelLocks[number].lock->Release();
		if(KernelLocks[number].lock->lock_queue->Size())
		toSend = (int *)KernelLocks[number].lock->lock_queue->Remove();

	}
	
	//on release wake up the next person as well as the sender
	
	
		char *data = "success";

		outPacketHeader.to = *toSend;
        outMailHeader.to = *toSend;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);
        
        

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);

}}


void serverCreateCondition(){

KConditionArrayLock->Acquire();
	int freeCondition = -1;//find a free lock
	for(int i = serverNumber * 1000; i < (serverNumber *1000 +999);i++){
		if(KernelConditions[i].open == true){
			freeCondition = i;
			break;}
	}

	if(freeCondition == -1){
		KConditionArrayLock->Release();
		//return freeCondition;
		}

	else{
		//set it to not free
		KernelConditions[freeCondition].open = false;
		//create lock
		KernelConditions[freeCondition].condition = new Condition("New Condition");
		//set address space
		KernelConditions[freeCondition].addrSpace = currentThread->space;
		//return index

		KConditionArrayLock->Release();
		//return freeCondition;
		}


		char *data = "default";

		net_buffer = itoa(freeCondition);
		strcpy(data,net_buffer.c_str());


	outPacketHeader.to = inPacketHeader.from;
	outMailHeader.to = inMailHeader.from;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
	
	printf("Sending \"%s\" to %d\n",data,outMailHeader.from);//take out after testing
	
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);


}

void serverDestroyCondition(int number){


KConditionArrayLock->Acquire();

	if(number < MAXCONDITIONS && number >= 0){
	//make sure number is valid
	if(KernelConditions[number].open == false){
		//make sure lock doesnt need to be used

		KernelConditions[number].condition = NULL;
		KernelConditions[number].open = true;//set to open
		KConditionArrayLock->Release();



	}
	else 
		printf("Condition number is invalid!\n");
}
	else  {
	printf("Condition number is invalid!\n");
	KConditionArrayLock->Release();}
	
	


		char *data = "success";

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);

}

void serverCreateMonitor(){


	KMonitorArrayLock->Acquire();
	int freeMonitor = -1;//find a free lock
	for(int i = serverNumber *1000; i < (serverNumber * 1000 + 999);i++){
		if(monitors[i].open == true){
			freeMonitor = i;
			break;}
	}

	if(freeMonitor == -1){
		KMonitorArrayLock->Release();
		//return freeMonitor;
		}

	else{
		//set it to not free
		monitors[freeMonitor].open = false;
		//create lock
		//return index

		KMonitorArrayLock->Release();
		printf("CREATING MONITOR VARIABLE %d\n",freeMonitor);
		//return freeMonitor;
		}




		char *data = "default";

		net_buffer = itoa(freeMonitor);
		strcpy(data,net_buffer.c_str());

	
	outPacketHeader.to = inPacketHeader.from;
	outMailHeader.to = inMailHeader.from;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
	
	printf("Sending \"%s\" to %d\n",data,outMailHeader.from);//take out after testing
	
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);

}

void serverRetrieveMonitor(int index){

 if(monitors[index].open == true){
	
	
int toServer = index / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend = "retrieveMonitor";
toSend.push_back('.');
toSend.append(itoa(index));

forward(toServer, fromClientPacket, fromClientMail, toSend);


}
else{
int toReturn = -1;
	//perform checks to make sure lock exists
	//lock exists
	//in current space
	//increment usage
	//call lock.acquire
	KMonitorArrayLock->Acquire();
	if(index >= MAXMONITORS){
		printf("Lock is out or range!\n");
		KMonitorArrayLock->Release();
	}

	
	else{
		toReturn = monitors[index].value;
		KMonitorArrayLock->Release();
	}
	
	
	char *data = "default";

		net_buffer = itoa(toReturn);
		strcpy(data,net_buffer.c_str());

	
	
	
	outPacketHeader.to = inPacketHeader.from;
	outMailHeader.to = inMailHeader.from;
	outMailHeader.length = strlen(data)+1;
	fflush(stdout);//always flush before and after sending
	
	printf("Sending \"%s\" to %d\n",data,outMailHeader.from);//take out after testing
	
	if(!postOffice->Send(outPacketHeader,outMailHeader,data)){
		printf("Send failed!\n");
	}
	fflush(stdout);
	

}
}


void serverSetMonitor(int index, int number){

 if(monitors[index].open == true){
	

	
int toServer = index / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend = "setMonitor";
        toSend.push_back('.');
        toSend.append(itoa(index));
        toSend.push_back(',');
        toSend.append(itoa(number));

forward(toServer, fromClientPacket, fromClientMail, toSend);

}

else{


//perform checks to make sure lock exists
	//lock exists
	//in current space
	//increment usage
	//call lock.acquire
	KMonitorArrayLock->Acquire();
	if(index >= MAXMONITORS){
		printf("Lock is out or range!\n");
		KMonitorArrayLock->Release();
	}

	
	
	else{
		printf("Setting Monitor %d to %d\n",index,number);
		monitors[index].value = number;
		KMonitorArrayLock->Release();

	}
	
	
	char *data = "success";

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);



}}



void serverSignalCondition(int index, int lockID){
int *toSend = &inMailHeader.from;

if(KernelConditions[index].open == true){
		
		
int toServer = index / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend  = "signalCondition";
        toSend.push_back('.');
        toSend.append(itoa(index));
        toSend.push_back(',');
        toSend.append(itoa(lockID));

forward(toServer, fromClientPacket, fromClientMail, toSend);	
		
		
		
		
		
	}
else{

KConditionArrayLock->Acquire();

	//get condition lock
	//check to make sure condition is valid
	if(index >= MAXCONDITIONS){
		printf("Condition is out or range!\n");
		KConditionArrayLock->Release();
		
	}
	
	KConditionArrayLock->Release();
	KLockArrayLock->Acquire();

	//get table lock lock

	//make sure lock is valid
	if(lockID >= MAXLOCKS){
		printf("Lock is out or range!\n");
		KLockArrayLock->Release();
	}

	else if(KernelLocks[lockID].open == true){
		printf("Lock has never been created!\n");
		KLockArrayLock->Release();
	}
	
	KLockArrayLock->Release();

	//KernelConditions[index].condition->Signal(KernelLocks[lockID].lock);
	if(KernelConditions[index].condition->wait_queue->Size() != 0)
	toSend = (int *)KernelConditions[index].condition->wait_queue->Remove();

	
	
	
	char *data = "success";

		outPacketHeader.to = *toSend;
        outMailHeader.to = *toSend;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);
        
        

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);



}}
void serverWaitCondition(int index, int lockID){


if(KernelConditions[index].open == true){
	

		
int toServer = index / 1000;
int fromClientPacket = inPacketHeader.from
int fromClientMail = inMailHeader.from;

string toSend  = "waitCondition";
        toSend.push_back('.');
        toSend.append(itoa(index));
        toSend.push_back(',');
        toSend.append(itoa(lockID));

forward(toServer, fromClientPacket, fromClientMail, toSend);	
		
		
}



	KConditionArrayLock->Acquire();

	//get condition lock
	//check to make sure condition is valid
	if(index >= MAXCONDITIONS){
		printf("Condition is out or range!\n");
		KConditionArrayLock->Release();
	}
	
	KConditionArrayLock->Release();
	KLockArrayLock->Acquire();

	//get table lock lock

	//make sure lock is valid
	if(lockID >= MAXLOCKS){
		printf("Lock is out or range!\n");
		KLockArrayLock->Release();
	}

	else if(KernelLocks[lockID].open == true){
		printf("Lock has never been created!\n");
		KLockArrayLock->Release();
	}
	else if(KernelLocks[lockID].addrSpace != currentThread->space) {
		printf("This lock is in the wrong address space!\n");
		KLockArrayLock->Release();
	}
	KLockArrayLock->Release();
	printf("Waiting With Lock %d \n" ,lockID);
	//KernelConditions[index].condition->Wait(KernelLocks[lockID].lock);
	KernelConditions[index].condition->wait_queue->Append(&inMailHeader.from);





}
void serverBroadcastCondition(int index, int lockID){

KConditionArrayLock->Acquire();

	//get condition lock
	//check to make sure condition is valid
	if(index >= MAXCONDITIONS){
		printf("Condition is out or range!\n");
		KConditionArrayLock->Release();
	}
	if(KernelConditions[index].open == true){
		printf("CONDITION IS NOT BEING USED!");
		KConditionArrayLock->Release();
	}
	KConditionArrayLock->Release();
	KLockArrayLock->Acquire();

	//get table lock lock

	//make sure lock is valid
	if(lockID >= MAXLOCKS){//make sure not outside range
		printf("Lock is out or range!\n");
		KLockArrayLock->Release();
	}

	else if(KernelLocks[lockID].open == true){//make sure being used
		printf("Lock has never been created!\n");
		KLockArrayLock->Release();
	}// make sure in valid address space
	else if(KernelLocks[lockID].addrSpace != currentThread->space) {
		printf("This lock is in the wrong address space!\n");
		KLockArrayLock->Release();
	}
	KLockArrayLock->Release();
	KernelConditions[index].condition->Broadcast(KernelLocks[lockID].lock);



char *data = "success";

		outPacketHeader.to = inPacketHeader.from;
        outMailHeader.to = inMailHeader.from;
        outMailHeader.length = strlen(data)+1;
        fflush(stdout);
        postOffice->Send(outPacketHeader,outMailHeader,data);

}


//----------------------------------------------------------------------
// Mail::Mail
//      Initialize a single mail message, by concatenating the headers to
//	the data.
//
//	"pktH" -- source, destination machine ID's
//	"mailH" -- source, destination mailbox ID's
//	"data" -- payload data
//----------------------------------------------------------------------

Mail::Mail(PacketHeader pktH, MailHeader mailH, char *msgData)
{
    ASSERT(mailH.length <= MaxMailSize);

    pktHdr = pktH;
    mailHdr = mailH;
    bcopy(msgData, data, mailHdr.length);
}

//----------------------------------------------------------------------
// MailBox::MailBox
//      Initialize a single mail box within the post office, so that it
//	can receive incoming messages.
//
//	Just initialize a list of messages, representing the mailbox.
//----------------------------------------------------------------------


MailBox::MailBox()
{ 
    messages = new SynchList(); 
}

//----------------------------------------------------------------------
// MailBox::~MailBox
//      De-allocate a single mail box within the post office.
//
//	Just delete the mailbox, and throw away all the queued messages 
//	in the mailbox.
//----------------------------------------------------------------------

MailBox::~MailBox()
{ 
    delete messages; 
}

//----------------------------------------------------------------------
// PrintHeader
// 	Print the message header -- the destination machine ID and mailbox
//	#, source machine ID and mailbox #, and message length.
//
//	"pktHdr" -- source, destination machine ID's
//	"mailHdr" -- source, destination mailbox ID's
//----------------------------------------------------------------------

static void 
PrintHeader(PacketHeader pktHdr, MailHeader mailHdr)
{
    printf("From (%d, %d) to (%d, %d) bytes %d\n",
    	    pktHdr.from, mailHdr.from, pktHdr.to, mailHdr.to, mailHdr.length);
}

//----------------------------------------------------------------------
// MailBox::Put
// 	Add a message to the mailbox.  If anyone is waiting for message
//	arrival, wake them up!
//
//	We need to reconstruct the Mail message (by concatenating the headers
//	to the data), to simplify queueing the message on the SynchList.
//
//	"pktHdr" -- source, destination machine ID's
//	"mailHdr" -- source, destination mailbox ID's
//	"data" -- payload message data
//----------------------------------------------------------------------

void 
MailBox::Put(PacketHeader pktHdr, MailHeader mailHdr, char *data)
{ 
    Mail *mail = new Mail(pktHdr, mailHdr, data); 

    messages->Append((void *)mail);	// put on the end of the list of 
					// arrived messages, and wake up 
					// any waiters
}

//----------------------------------------------------------------------
// MailBox::Get
// 	Get a message from a mailbox, parsing it into the packet header,
//	mailbox header, and data. 
//
//	The calling thread waits if there are no messages in the mailbox.
//
//	"pktHdr" -- address to put: source, destination machine ID's
//	"mailHdr" -- address to put: source, destination mailbox ID's
//	"data" -- address to put: payload message data
//----------------------------------------------------------------------

void 
MailBox::Get(PacketHeader *pktHdr, MailHeader *mailHdr, char *data) 
{ 
    DEBUG('n', "Waiting for mail in mailbox\n");
    Mail *mail = (Mail *) messages->Remove();	// remove message from list;
						// will wait if list is empty

    *pktHdr = mail->pktHdr;
    *mailHdr = mail->mailHdr;
    if (DebugIsEnabled('n')) {
	printf("Got mail from mailbox: ");
	PrintHeader(*pktHdr, *mailHdr);
    }
    bcopy(mail->data, data, mail->mailHdr.length);
					// copy the message data into
					// the caller's buffer
    delete mail;			// we've copied out the stuff we
					// need, we can now discard the message
}

//----------------------------------------------------------------------
// PostalHelper, ReadAvail, WriteDone
// 	Dummy functions because C++ can't indirectly invoke member functions
//	The first is forked as part of the "postal worker thread; the
//	later two are called by the network interrupt handler.
//
//	"arg" -- pointer to the Post Office managing the Network
//----------------------------------------------------------------------

static void PostalHelper(int arg)
{ PostOffice* po = (PostOffice *) arg; po->PostalDelivery(); }
static void ReadAvail(int arg)
{ PostOffice* po = (PostOffice *) arg; po->IncomingPacket(); }
static void WriteDone(int arg)
{ PostOffice* po = (PostOffice *) arg; po->PacketSent(); }

//----------------------------------------------------------------------
// PostOffice::PostOffice
// 	Initialize a post office as a collection of mailboxes.
//	Also initialize the network device, to allow post offices
//	on different machines to deliver messages to one another.
//
//      We use a separate thread "the postal worker" to wait for messages 
//	to arrive, and deliver them to the correct mailbox.  Note that
//	delivering messages to the mailboxes can't be done directly
//	by the interrupt handlers, because it requires a Lock.
//
//	"addr" is this machine's network ID 
//	"reliability" is the probability that a network packet will
//	  be delivered (e.g., reliability = 1 means the network never
//	  drops any packets; reliability = 0 means the network never
//	  delivers any packets)
//	"nBoxes" is the number of mail boxes in this Post Office
//----------------------------------------------------------------------

PostOffice::PostOffice(NetworkAddress addr, double reliability, int nBoxes)
{
// First, initialize the synchronization with the interrupt handlers
    messageAvailable = new Semaphore("message available", 0);
    messageSent = new Semaphore("message sent", 0);
    sendLock = new Lock("message send lock");

// Second, initialize the mailboxes
    netAddr = addr; 
    numBoxes = nBoxes;
    boxes = new MailBox[nBoxes];

// Third, initialize the network; tell it which interrupt handlers to call
    network = new Network(addr, reliability, ReadAvail, WriteDone, (int) this);


// Finally, create a thread whose sole job is to wait for incoming messages,
//   and put them in the right mailbox. 
    Thread *t = new Thread("postal worker");

    t->Fork(PostalHelper, (int) this);
}

//----------------------------------------------------------------------
// PostOffice::~PostOffice
// 	De-allocate the post office data structures.
//----------------------------------------------------------------------

PostOffice::~PostOffice()
{
    delete network;
    delete [] boxes;
    delete messageAvailable;
    delete messageSent;
    delete sendLock;
}

//----------------------------------------------------------------------
// PostOffice::PostalDelivery
// 	Wait for incoming messages, and put them in the right mailbox.
//
//      Incoming messages have had the PacketHeader stripped off,
//	but the MailHeader is still tacked on the front of the data.
//----------------------------------------------------------------------

void
PostOffice::PostalDelivery()
{
    PacketHeader pktHdr;
    MailHeader mailHdr;
    char *buffer = new char[MaxPacketSize];

    for (;;) {
        // first, wait for a message
        messageAvailable->P();	
        pktHdr = network->Receive(buffer);

        mailHdr = *(MailHeader *)buffer;
        if (DebugIsEnabled('n')) {
	    printf("Putting mail into mailbox: ");
	    PrintHeader(pktHdr, mailHdr);
        }

	// check that arriving message is legal!
	ASSERT(0 <= mailHdr.to && mailHdr.to < numBoxes);
	ASSERT(mailHdr.length <= MaxMailSize);

	// put into mailbox
        boxes[mailHdr.to].Put(pktHdr, mailHdr, buffer + sizeof(MailHeader));
    }
}

//----------------------------------------------------------------------
// PostOffice::Send
// 	Concatenate the MailHeader to the front of the data, and pass 
//	the result to the Network for delivery to the destination machine.
//
//	Note that the MailHeader + data looks just like normal payload
//	data to the Network.
//
//	"pktHdr" -- source, destination machine ID's
//	"mailHdr" -- source, destination mailbox ID's
//	"data" -- payload message data
//----------------------------------------------------------------------

bool
PostOffice::Send(PacketHeader pktHdr, MailHeader mailHdr, char* data)
{
    char* buffer = new char[MaxPacketSize];	// space to hold concatenated
						// mailHdr + data

    if (DebugIsEnabled('n')) {
	printf("Post send: ");
	PrintHeader(pktHdr, mailHdr);
    }

    ASSERT(mailHdr.length <= MaxMailSize);
    ASSERT(0 <= mailHdr.to && mailHdr.to < numBoxes);
    
    // fill in pktHdr, for the Network layer
    pktHdr.from = netAddr;
    pktHdr.length = mailHdr.length + sizeof(MailHeader);

    // concatenate MailHeader and data
    bcopy((char *) &mailHdr, buffer, sizeof(MailHeader));
    bcopy(data, buffer + sizeof(MailHeader), mailHdr.length);

    sendLock->Acquire();   		// only one message can be sent
					// to the network at any one time
    bool success = network->Send(pktHdr, buffer);
    messageSent->P();			// wait for interrupt to tell us
					// ok to send the next message
    sendLock->Release();

    delete [] buffer;			// we've sent the message, so
					// we can delete our buffer
    return success;
}

//----------------------------------------------------------------------
// PostOffice::Send
// 	Retrieve a message from a specific box if one is available, 
//	otherwise wait for a message to arrive in the box.
//
//	Note that the MailHeader + data looks just like normal payload
//	data to the Network.
//
//
//	"box" -- mailbox ID in which to look for message
//	"pktHdr" -- address to put: source, destination machine ID's
//	"mailHdr" -- address to put: source, destination mailbox ID's
//	"data" -- address to put: payload message data
//----------------------------------------------------------------------

void
PostOffice::Receive(int box, PacketHeader *pktHdr, 
				MailHeader *mailHdr, char* data)
{
    ASSERT((box >= 0) && (box < numBoxes));

    boxes[box].Get(pktHdr, mailHdr, data);
    ASSERT(mailHdr->length <= MaxMailSize);
}

//----------------------------------------------------------------------
// PostOffice::IncomingPacket
// 	Interrupt handler, called when a packet arrives from the network.
//
//	Signal the PostalDelivery routine that it is time to get to work!
//----------------------------------------------------------------------

void
PostOffice::IncomingPacket()
{ 
    messageAvailable->V(); 
}

//----------------------------------------------------------------------
// PostOffice::PacketSent
// 	Interrupt handler, called when the next packet can be put onto the 
//	network.
//
//	The name of this routine is a misnomer; if "reliability < 1",
//	the packet could have been dropped by the network, so it won't get
//	through.
//----------------------------------------------------------------------

void 
PostOffice::PacketSent()
{ 
    messageSent->V();
}
